package com.hmall.search.service.impI;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.alibaba.fastjson.JSON;
import com.hmall.common.client.item.ClientFeign;
import com.hmall.common.contans.MqContants;
import com.hmall.common.dto.PageDTO;
import com.hmall.common.pojo.item.Item;
import com.hmall.search.pojo.ItemDoc;
import com.hmall.search.service.SearchService;
import com.hmall.search.web.Request.RequestParams;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.completion.CompletionSuggestion;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpI implements SearchService {

    @Resource
    private RestHighLevelClient client;



    /**
     * 自动补全
     * @param key
     * @return
     */
    @Override
    public List<String> getSuggestions(String key) {
        try {
            //构造查询
            SearchRequest request = new SearchRequest("hmsc");
            request.source().suggest(new SuggestBuilder()
                    .addSuggestion("suggestions",
                            SuggestBuilders.completionSuggestion("suggestion")
                                    .prefix(key)
                                    .skipDuplicates(true)
                                    .size(10)));
            //发起请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            //解析
            Suggest suggest = response.getSuggest();
            CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");

            List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
            List<String> list = new ArrayList<>();
            for (CompletionSuggestion.Entry.Option option : options) {
                String str = option.getText().toString();
                list.add(str);
            }
            return list;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }


    /**
     * 聚合
     *
     * @param params
     * @return
     */
    @Override
    public Map<String, List<String>> getFilters(RequestParams params) {
        try {
            // 1.准备Request
            SearchRequest request = new SearchRequest("hmsc");
            // 2.准备DSL
            // 构建BooleanQuery
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            // 2.1.query
            buildBasicQuery(params, request,boolQuery);
            // 2.2.设置size
            request.source().size(0);
            // 2.3.聚合
            buildAggregation(request);
            // 3.发出请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            // 4.解析结果
            Map<String, List<String>> result = new HashMap<>();
            Aggregations aggregations = response.getAggregations();
            // 4.1.根据品牌名称，获取品牌结果
            List<String> brandList = getAggByName(aggregations, "brandAgg");
            result.put("brand", brandList);
            // 4.2.根据分类名称，获取分类结果
            List<String> starList = getAggByName(aggregations, "categoryAgg");
            result.put("category", starList);

            return result;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 分页查询，排序，关键字，高亮,竞价排名
     *
     * @param params
     * @return
     */
    @Override
    public PageDTO<ItemDoc> search(RequestParams params) {
        try {
            // 1.准备Request
            SearchRequest request = new SearchRequest("hmsc");
            // 1.构建BooleanQuery
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            //1.1过滤
            buildBasicQuery(params,request,boolQuery);


            //2排序
            if (StringUtils.isNotBlank(params.getSortBy()) && !StringUtils.equals("default", params.getSortBy())) {

               if(StringUtils.equals("sold",params.getSortBy())) {
                   request.source().sort(params.getSortBy(), SortOrder.DESC);
               }else if (StringUtils.equals("price",params.getSortBy())){
                    request.source().sort(params.getSortBy(), SortOrder.ASC);

                }
            }

            // 3.分页
            int page = params.getPage();
            int size = params.getSize();
            request.source().from((page - 1) * size).size(size);

            // 4-高亮
            request.source().highlighter(new HighlightBuilder()
                    .field("name")
                    .requireFieldMatch(false));

            // 5.发送请求
            SearchResponse response = client.search(request, RequestOptions.DEFAULT);
            // 6.解析响应
            return handleResponse(response);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 解析响应数据
     * @param response
     * @return
     */
    private PageDTO handleResponse(SearchResponse response) {
        // 4.解析响应
        SearchHits searchHits = response.getHits();
        // 4.1.获取总条数
        long total = searchHits.getTotalHits().value;
        // 4.2.文档数组
        SearchHit[] hits = searchHits.getHits();
        // 4.3.遍历
        List<ItemDoc> items = new ArrayList<>();
        for (SearchHit hit : hits) {
            // 获取文档source
            String json = hit.getSourceAsString();
            // 反序列化
            Item itemDoc = JSON.parseObject(json, Item.class);
            // 放入集合
            //4.4获取高亮数据
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (null != highlightFields && highlightFields.containsKey("name")) {
                String name = highlightFields.get("name").getFragments()[0].string();
                itemDoc.setName(name);
            }

            ItemDoc itemDoc1= new ItemDoc(itemDoc);
            items.add(itemDoc1);
        }
        // 4.5.封装返回
        return new PageDTO(total, items);
    }

    /**
     * 对那些字段进行聚合
     * @param request
     */
    private void buildAggregation(SearchRequest request) {
        request.source().aggregation(AggregationBuilders
                .terms("brandAgg")
                .field("brand")
                .size(100)
        );
        request.source().aggregation(AggregationBuilders
                .terms("categoryAgg")
                .field("category")
                .size(100)
        );

    }

    /**
     * 获取聚合字段值
     * @param aggregations
     * @param aggName
     * @return
     */
    private List<String> getAggByName(Aggregations aggregations, String aggName) {
        // 4.1.根据聚合名称获取聚合结果
        Terms brandTerms = aggregations.get(aggName);
        // 4.2.获取buckets
        List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
        // 4.3.遍历
        List<String> brandList = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            // 4.4.获取key
            String key = bucket.getKeyAsString();
            brandList.add(key);
        }
        return brandList;
    }

    /**
     * 过滤筛选哪些字段
     * @param params
     * @param request
     */
    private void buildBasicQuery(RequestParams params, SearchRequest request,BoolQueryBuilder boolQuery) {

        // 2.关键字搜索
        String key = params.getKey();
        if (key == null || "".equals(key)) {
            boolQuery.must(QueryBuilders.matchAllQuery());
        } else {
            boolQuery.must(QueryBuilders.matchQuery("all", key));
        }

        // 4.品牌条件
        if (params.getBrand() != null && !params.getBrand().equals("")) {
            boolQuery.filter(QueryBuilders.termQuery("brand", params.getBrand()));
        }
        // 4.分类条件
        if (params.getCategory() != null && !params.getCategory().equals("")) {
            boolQuery.filter(QueryBuilders.termQuery("category", params.getCategory()));
        }
        // 6.价格
        if (params.getMinPrice() != null && params.getMaxPrice() != null) {
            boolQuery.filter(QueryBuilders
                    .rangeQuery("price")
                    .gte(params.getMinPrice())
                    .lte(params.getMaxPrice())
            );
        }

        //竞争排名,已经给id为【4034064，4034901，3848955，3773053】 设置为true
        FunctionScoreQueryBuilder functionScoreQuery =
                QueryBuilders.functionScoreQuery(
                        // 原始查询，相关性算分的查询
                        boolQuery,
                        // function score的数组
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                                // 其中的一个function score 元素
                                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                        // 过滤条件
                                        QueryBuilders.termQuery("isAD", true),
                                        // 算分函数
                                        ScoreFunctionBuilders.weightFactorFunction(100)
                                )
                        });
        // 7.放入source
        request.source().query(functionScoreQuery);
    }


    /**
     * 商品上架时，进行添加
     * @param id
     */
    @Resource
    private ClientFeign clientFeign;

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = MqContants.HOTEL_INSERT_QUEUE),
            exchange = @Exchange(name = MqContants.HOTEL_EXCHANGE, type = ExchangeTypes.TOPIC),
            key = {MqContants.HOTEL_INSERT_KEY}
    ))
    public void itemAdd(Long id) {
        try {

            Item item = clientFeign.seleceById(id);
//            item.setIsAd(0);
            //将数据转为文档类型
            ItemDoc itemDoc = new ItemDoc(item);
            //转json
            String json = JSON.toJSONString(itemDoc);
            //准备request对象
            IndexRequest request = new IndexRequest("hmsc")
                    .id(itemDoc.getId().toString());
            request.source(json, XContentType.JSON);
            client.index(request, RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 下架的时候删除
     * @param id
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = MqContants.HOTEL_DELETE_QUEUE),
            exchange = @Exchange(name = MqContants.HOTEL_EXCHANGE, type = ExchangeTypes.TOPIC),
            key = {MqContants.HOTEL_DELETE_KEY}
    ))
    public void delete(String id){
        try {
            DeleteRequest request = new DeleteRequest("hmsc", id);
            client.delete(request,RequestOptions.DEFAULT);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
